/*
 * ml20dll
 * This source code is under the GNU Public License (GPL).
 * Written by Ramiro Polla
 * Initial release: july 14th, 2005
 *
 * this dll reads ml20 files
 */

#ifndef ML20DLL_H
#define ML20DLL_H

#define VERSION_STRING "ml20dll - 1.0rc4"

#include <windows.h>
#include <vfw.h>
#include <stdio.h>
#include <inttypes.h>

/**************************** Structures **********************************/

typedef struct kfret
{
    char imagebuffer[0x38400];
    int height;
    int width;
} imgret_t, *imgret_p;

typedef struct msn_s {
    HANDLE rp;
    DWORD pid;
    DWORD procaddress;
    char *wmv;
} msn_t;

typedef struct {
    //kernel functions
    HMODULE (__stdcall *LoadLibraryX)(LPCSTR);
    BOOL (__stdcall *FreeLibrary)(HMODULE);
    FARPROC (__stdcall *GetProcAddress)(HMODULE, LPCSTR);

    //vfw functions
    BOOL (__stdcall *ICInstall)( DWORD fccType,DWORD fccHandler, LPARAM lParam, LPSTR szDesc, UINT wFlags );
    HIC (__stdcall *ICOpen)( DWORD fccType, DWORD fccHandler, UINT wMode );
    DWORD (__cdecl *ICDecompress) ( HIC hic, DWORD dwFlags, LPBITMAPINFOHEADER lpbiFormat, LPVOID lpData, LPBITMAPINFOHEADER lpbi, LPVOID lpBits );
    LRESULT (__stdcall *ICClose)( HIC hic );
    LRESULT (__stdcall *ICSendMessage)( HIC hic, UINT wMsg, DWORD dw1, DWORD dw2 );
    BOOL (__stdcall *ICRemove)( DWORD fccType, DWORD fccHandler, UINT wFlags );

    //function names (there has to be a better way)
    char vfw[16];
    char icopen[7];
    char icinstall[10];
    char icdecompress[13];
    char icclose[8];
    char icsendmessage[14];
    char icremove[9];

    //headers
    BITMAPINFOHEADER ML20;
    BITMAPINFOHEADER BITM;

    //pointers to injdata and threads inside msn messenger
    LPVOID    remotedata;
    LPVOID    remoteout;
    LPVOID    remotecode;
    uint8_t *in;
    uint8_t *out;

    //ML20's HIC
    HIC hIC;

    //address of ml20 codec inside msn messenger
    // DEPENDS ON MSN VERSION
    long ml20proc;

    HINSTANCE vfwdll;
    msn_t msnt;
} INJDATA;

struct wmv3bmi
{
    BITMAPINFOHEADER bm;
    uint8_t data[6];    //last 6 bytes are still unknown
};

// ML20 header struct
typedef struct {
    uint16_t    ss;        //struct size
    uint16_t    width;    //frame width
    uint16_t    height;    //frame height
    uint16_t    ff;        //kf + some other info
    uint32_t    size;    //size of data
    uint32_t    signature;    //ML20
    uint32_t    u3;        //?
    uint32_t    time;    //time
} ML20H;

// ML20 frame struct
typedef struct {
    ML20H h;
    uint8_t *data;
    unsigned int size;
} ML20F;

// first bytes of ML20 frame
typedef struct {
    uint16_t coeffs;
    uint16_t quality;
    int16_t width;
    int16_t height;
    uint32_t constant;
    uint32_t nkf;
} frameinfo;

/* UDP header*/
struct udphdr {
    u_short uh_sport;          // Source port
    u_short uh_dport;          // Destination port
    u_short uh_len;            // Datagram length
    u_short uh_crc;            // Checksum
};

#pragma pack( push, ml20dll_pack )
#pragma pack(1)
//      TTTTTTTTTTT FF NN TO
//05 49 2d 3d 87 02 0a 05 06
struct udpinthdr {
    u_short unk;
    uint32_t time;
    uint8_t frame_num;
    uint8_t frame_part;
    uint8_t frame_tot;
};
#pragma pack( pop, ml20dll_pack )

//virtual ml20 frame
typedef struct vframe_s
{
    struct vframe_s *nf;    // next frame
    struct vframe_s *pf;    // previous frame
    struct vframe_s *nkf;    // next key frame
    struct vframe_s *mkf;    // my keyframe
    struct vframe_s *pkf;    // previous key frame
    unsigned int filepos;    // position in file // or (udps *)
    unsigned int fnum;        // frame number
    unsigned int time;        // frame time
    unsigned int real_time;    // fixed time (starting at 0)
} vframe_t;

//virtual ml20 stream
typedef struct vstream_s
{
    FILE *file;        // FILE
    vframe_t *ff;    // first frame
    vframe_t *lf;    // last frame

    LONG width;
    LONG height;

    char *fullname;    // full file name
    unsigned int eof;

    int flags;
} vstream_t;

#define VSTREAM_UDP        (1 << 0)
#define VSTREAM_VC        (1 << 1)

typedef struct decoder_s
{
    BITMAPINFOHEADER ML20;    // ML20 BITMAPINFOHEADER
    BITMAPINFOHEADER BITM;    // BITM BITMAPINFOHEADER
    struct wmv3bmi WMV3;
    HIC hIC;                // Codec Handle

    uint8_t *kol;                // output buffer

    vstream_t *vstream;        // last vstream used
    vframe_t *vframe;        // last vframe used
    vframe_t *kframe;        // last keyframe decoded
    vframe_t *dvframe;        // last actually decoded vframe

    vframe_t *in;            // in frame
    vframe_t *out;            // out frame

    INJDATA *injdata;

    int flags;
} decoder_t;

#define DECODER_SELECTION    (1 << 1)
#define DECODER_REMOTE        (1 << 2)

#pragma pack( push, ml20dll_pack )
#pragma pack( 1 )
struct vcinthdr
{
    uint8_t code;    //code
    uint16_t retransmission:5;    //(0000000000011111) retransmission on case of error
    uint16_t size:11;    //(1111111111100000) payload size

    uint8_t frame_part:6;    //(00111111) frame part
    uint8_t nkeyframe:2;    //(11000000) !keyframe?

    uint32_t timestamp;    //timestamp
    uint8_t frame_num;    //frame number
    uint8_t frame_tot;    //packets in frame
};
#pragma pack( pop, ml20dll_pack )

typedef struct udp_structs
{
    struct udp_structs *next;        // next udp_structs
    struct udp_structs *previous;    // previous udp_structs
    struct udpinthdr udp_hdr;        // frame information
    unsigned int filepos;            // position on file after frame information
    unsigned int size;                // number of bytes to read at filepos
    unsigned int length;            // size + sizeof(udp_hdr)
} udps;

typedef struct vc_structs
{
    struct vc_structs *next;        // next vc_structs
    struct vc_structs *previous;    // previous vc_structs
    struct vcinthdr vc_hdr;        // frame information
    unsigned int filepos;            // position on file after frame information
} vcs;

typedef struct
{
    uint16_t    one;
    uint16_t    quality;
    uint16_t    width;
    uint16_t    height;
    uint16_t    two;
    uint16_t    three;
    uint16_t    nkf;
} ml20_int_h;

/***************************** Macros *************************************/

#define IS_SET(vector, flag) ((vector) & (flag))
#define SET_BIT(vector, flag) ((vector) |= (flag))
#define UNSET_BIT(vector, flag) ((vector) &= (-1) - (flag))

#define READ_DATA            (1 << 0)
#define READ_SEEK_IN        (1 << 1)
#define READ_SEEK_OUT        (1 << 2)
#define READ_SEEK_FIRST        (1 << 3)
#define READ_RET_IN            (1 << 4)
#define READ_UDP            (1 << 5)

#define FOURCC_ML20 MAKEFOURCC( 'M', 'L', '2', '0' )
#define FOURCC_LM20 MAKEFOURCC( 'L', 'M', '2', '0' )
#define FOURCC_WMV3 MAKEFOURCC( 'W', 'M', 'V', '3' )

/************************* Function prototypes ****************************/

/* vc.c */
vcs *vc_next_keyframe( vcs *frame, FILE *in, ML20F *ml20f );
vcs *vc_next_frame( vcs *tmp, FILE *in, ML20F *ml20f );
vcs *read_vcs( FILE *in, int pos );
void clean_vcs( vcs *first_u );
vcs *fit_vcs( vcs *first_u, vcs *new_u );
void fit_vcs_part( vcs **first_u, vcs **last_u, vcs *new_u );

/* stream_file.c */
void dump_frame( unsigned int filepos, vstream_t *vstream, ML20F *ml20f );
int read_next_keyframe( vstream_t *vstream, ML20F *ml20f, int flags, udps *frame );
int read_next_frame( vstream_t *vstream, ML20F *ml20f, int flags, udps *frame );
imgret_p ml20_first_keyframe( char *fullname, msn_t *msnt );

int ml20_next_keyframe( FILE *inf, ML20F *ml20f, int flags );
int ml20_next_frame( FILE *inf, ML20F *ml20f, int flags );

/* udp.c */
udps *udp_next_keyframe( udps *frame, FILE *in, ML20F *ml20f );
udps *udp_next_frame( udps *tmp, FILE *in, ML20F *ml20f );
udps *read_udps( FILE *in, int pos );
void clean_udps( udps *first_u );
udps *fit_udps( udps *first_u, udps *new_u );
void fit_udps_part( udps **first_u, udps **last_u, udps *new_u );

/* vstream.c */
int decode_frame( decoder_t *decoder, vframe_t *vframe );
int decode_keyframe( decoder_t *decoder, vframe_t *vframe );
vstream_t *build_vstream( char *fname );
int vstream_new_frame( vstream_t *vstream );
void kill_vstream( vstream_t *vstream );
vframe_t *vstream_add_frame( vstream_t *vstream, int filepos, ML20F *ml20f );

/* decoder.c */
decoder_t *new_decoder( vstream_t *vstream, msn_t *msnt );
void kill_decoder( decoder_t *decoder );

/* threads.c */
int svt_address( void );
int dvt_address( void );
int evt_address( void );
int svt_size( void );
int dvt_size( void );
int evt_size( void );

/* remote.c */
int startremotevideo( INJDATA *injdata );
int decoderemotevideo( INJDATA *injdata, uint8_t *data, int size );
int endremotevideo( INJDATA *injdata );

char *ml20dll_version( );

#endif
